iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0
自我挑戰組

Terraform 繁體中文系列 第 13

Day13-【入門教程】重載文件, 程式碼風格規範, Checks

  • 分享至 

  • xImage
  •  

原簡體中文教程連結: Introduction.《Terraform入門教程》


1.4.9.1. 重載文件

一般來說 Terraform 會載入模組內所有的 .tf.tf.json 文件,並要求文件內定義了一組無重複的物件。如果兩個檔案嘗試定義同一個對象,那麼 Terraform 會報錯。

在某些少見場景中,能夠用單獨的檔案重載已有物件配置的特定部分將會十分有用。比如說,由工程師編寫的設定檔能夠在執行時被程式產生的 JSON 檔案部分重載。

為支援這些少見場景,Terrform 會對後綴名為 override.tfoverride.tf.json 的程式碼檔案進行特殊處理。對於名為 override.tfoverride.tf.json 的程式碼檔案也會進行相同的特殊處理。

Terraform 一開始載入程式碼檔案時會跳過這些重載文件,然後才會依照字典順序一個一個處理重載文件。對重載檔案中定義的所有頂級區塊(resource、data 等),Terraform 會嘗試尋找對應的已有物件並且將重載內容合併進已有物件。

重載檔案只應使用於特殊場景,過度使用會使得讀者在閱讀原始程式碼檔案時被迫還要閱讀所有的重載檔案才能理解物件配置,從而降低了程式碼的可讀性。使用重載文件時,請在原始文件被重載的部分中添加相應註釋,提醒未來的讀者哪些部分會被重載文件修改。

1.4.9.1.1. 例子

如果我們有一個名為 example.tf 的程式碼檔案:

resource "aws_instance" "web" {
  instance_type = "t2.micro"
  ami           = "ami-408c7f28"
}

然後我們建立一個名為 override.tf 的檔案:

resource "aws_instance" "web" {
  ami = "foo"
}

Terraform 隨後會合併兩者,實際的配置會是這樣的:

resource "aws_instance" "web" {
  instance_type = "t2.micro"
  ami           = "foo"
}

1.4.9.1.2. 合併行為

不同的區塊類型有著微不同的合併行為,某些特定區塊內的特殊構造會以特殊形式被合併。

一般來說:

  • 重載檔案內的頂級區塊會和普通檔案內同類型同名的頂級區塊合併
  • 重載檔案內的頂級區塊設定冊參數會覆寫普通檔案內對應區塊內的同名參數
  • 重載區塊內的內嵌區塊會取代普通檔案內對應區塊內的所有同類型內嵌區塊。所有重載區塊內沒有定義的內嵌區塊在普通檔案內保持不變
  • 內嵌塊的內容不會進行合併
  • 合併後的區塊仍然需要符合對應區塊類型的所有驗證規則

如果有多個重載檔案定義了同一個頂級區塊,那麼重載效果是疊加的,後載入的重載區塊會在先前載入的重載區塊生效的基礎上合併。重載操作首先依照檔案名稱的字典序其次是在重載檔案中的位置決定執行順序。

有一些針對特定頂級區塊類型的特殊合併行為規則,我們將重載檔案中定義的區塊稱為重載區塊,重載區塊在普通檔案中對應的區塊稱為來源區塊:

1.4.9.1.2.1. 合併 resource 區塊以及合併 data 區塊

resource 區塊內,所有 lifecycle 區塊的內容會依照參數逐條合併。比如說,一個重載塊只定義了 create_before_destroy 參數而源塊定義了 ignore_changes,那麼 create_before_destroy 被合併的同時 igonore_changes 將會被保留。

如果重載的 resource 區塊包含了一個或多個 provisioner,那麼來源區塊內所有的 provisioner 會被忽略。

如果重載的 resource 區塊內包含了一個 connection 區塊,那麼它將完全覆蓋所有來源區塊內定義的 connection 區塊

不允許在重載區塊內定義 depends_on 參數,那將會引發一個錯誤。

1.4.9.1.2.2. 合併 variable 區塊

variable 區塊內參數的合併遵循上述的標準流程,但對於 typedefault 參數的處理會有一些特殊的考慮。

如果來源區塊定義了 default 值而重載區塊修改了變數的 type,Terraform 會嘗試將 default 值轉換成新類型,如果轉換失敗則會報錯。

同樣的,如果來源區塊定義了 type 參數而重載區塊修改了 default 值,那麼新的 default 值必須能夠轉換成原先的型別。

1.4.9.1.2.3. 合併 output 區塊

不允許在重載區塊內定義 depends_on 參數,這會引發一個錯誤。

1.4.9.1.2.4. 合併 locals 區塊

所有的 locals 區塊都定義了一個或多個命名值。針對 locals 的合併會是依照命名值的名字逐條執行的,不論命名值是在哪個 locals 區塊內被定義的。

1.4.9.1.2.5. 合併 terraform 區塊

如果重載區塊定義了 required_providers 參數,那麼它的值會被逐條合併,這就允許重載區塊在不影響其他 Provider 的情況下調整單一 Provider 的版本約束。

重載區塊內的 requeired_versionrequired_providers 里的配置完全覆蓋來源區塊內的相應配置。如果來源區塊和重載區塊都定義了 required_version,那麼來源區塊的配置就會被完全忽略。


1.4.10.1. 程式碼風格規範

Terraform 推薦以下程式碼規格:

  • 使用兩個空格縮排
  • 同一縮排層級的多個賦值語句以等號對齊:
ami           = "abc123"
instance_type = "t2.micro"
  • 當塊體內同時有參數賦值以及內嵌塊時,請先寫參數賦值,然後是內嵌塊。參數與內嵌塊之間空一行分隔
  • 同時包含參數賦值以及元參數賦值的區塊,請先編寫元參數賦值語句,接著是參數賦值語句,之間空一行分隔。元參數區塊請置於區塊體的最後,空一行分隔:
resource "aws_instance" "example" {
  count = 2 # meta-argument first

  ami           = "abc123"
  instance_type = "t2.micro"

  network_interface {
    # ...
  }

  lifecycle { # meta-argument block last
    create_before_destroy = true
  }
}
  • 頂層區塊之間應空一行分隔。內嵌塊之間也應該空一行分隔,除非是相同類型的內嵌塊(例如 resource 塊內部多個 provisioner 塊)
  • 同類型區塊之間盡量避免插入其他類型區塊,除非不同類型區塊共同組成了一個有語義的家族(比方說,aws_instnace 資源內的 root_block_deviceebs_block_deviceephemeral_block_device 內嵌區塊共同構成了描述 AWS 區塊儲存的區塊家族,所以他們可以被混合編寫)。

1.4.11.1. Checks

check 區塊是 Terraform 1.5 開始引入的新功能。

過去我們可以在 resource 區塊裡的 lifecycle 區塊中驗證基礎設施的狀態。check 區塊填補了在 terraform apply 後驗證基礎設施狀態這項功能中的一塊空白。

check 區塊允許我們定義在每次 plan 以及 apply 操作後執行的自訂的驗證。check 區塊定義的驗證邏輯是作為 planapply 操作的最後一步執行的。

1.4.11.1.1. 語法

你可以定義一個包含本地名稱的 check 區塊,其中可以定義一個有限作用範圍的 data 區塊,以及至少一個的斷言

下面的範例示範了載入 Terraform 官網並驗證 HTTP 回傳狀態碼為 200

check "health_check" {
  data "http" "terraform_io" {
    url = "https://www.terraform.io"
  }

  assert {
    condition = data.http.terraform_io.status_code == 200
    error_message = "${data.http.terraform_io.url} returned an unhealthy status code"
  }
}

1.4.11.1.1.1. 有限作用範圍的資料來源

我們可以在 check 區塊使用任意 Provider 提供的任意資料來源作為一個有限作用範圍的資料來源。

一個 check 區塊可以配一個可選的內嵌(也叫有限作用範圍)資料來源。該 data 區塊和普通的 data 區塊行為類似,但你不能在定義它的 check 區塊以外引用它。另外,如果一個有限作用範圍的資料來源運行時觸發了任意錯誤,這些錯誤將被標記為警告,不會阻止 Terraform 繼續執行操作。

你可以使用有限作用範圍的資料來源在 resourcelifecycle 外驗證相關基礎設施片段的狀態。在上面的例子裡,如果 terraform_io 資料來源在載入時發生錯誤,那麼我們將會收到一個警告而不是中斷執行的錯誤。

元參數

有限作用域的資料來源支援 depends_onprovider 元參數,但不支援 countfor_each 元參數。

depends_on

depends_on 元參數配合有限作用域資料來源可以提供非常強大的能力。

假設上述範例中的 Terraform 網站是我們即將用同一目錄下的 Terraform 程式碼部署的,在第一次創建 Plan 時因為網站還沒有被創建,所以驗證會失敗,Terraform 總是會在一開始顯示一條讓人分心的警告訊息。

我們可以為該內嵌資料來源新增 depends_on 來確保該資料來源依賴某項組成基礎架構的必要資源,例如負載平衡器。這樣對該資料來源的檢查結果將保持 known after apply 直到依賴項建立完成。此策略避免了在配置階段產生無意義的警告訊息,直到在 planapply 操作的合適階段執行檢查。

該策略的一個問題是如果有限作用域資料來源所依賴的資源發生了變化,那麼 check 區塊將返回 known after apply 直到 Terraform 完成了對被依賴資源的更新。在某些情況下,這種行為將會引發一些問題。

我們推薦只有在內嵌資料來源依賴某項資源,但又沒有明確的引用其資料時使用 depends_on 元參數。

1.4.11.1.1.2. 斷言

我們在 check 區塊中使用 assert 區塊定義自訂的斷言條件。每個 check 區塊必須聲明至少一個或更多的 assert 區塊。每個 assert 區塊都包含了一個 condition 屬性與一個 error_message 屬性。

與其他自訂檢查(variable 中的 validation 以及 lifecycle 中的 preconditionpostcondition)不同,assert 的斷言不會影響 Terraform 執行操作。失敗的斷言將以警告訊息的形式輸出而不會中斷後續的操作。這與其他諸如 postcondition 這樣的自訂檢查形成了對比,因為它們的檢查失敗會立即終止後續的 plan 以及 apply 操作,傳回錯誤訊息。

assert 區塊中的斷言條件表達式可以引用同一 check 區塊裡的內嵌資料來源數據,以及同一模組中的任意輸入參數、資源、資料來源、模組的輸出值。

1.4.11.1.1.3. check 區塊的元參數

check 塊目前不支援元參數。Terraform 團隊目前正在收集有關此功能的回饋。

1.4.11.1.2. 是使用check 區塊還是其他自訂條件檢查

check 區塊提供了 Terraform 中最靈活的驗證功能。我們可以在其中引用輸出值、輸入參數、資源以及資料來源的值。我們的確可以使用 check 區塊取代所有其他的自訂條件檢查,但這並不意味著我們應該這麼做。

check 與其他檢查最大的差別在於 check 區塊不會中斷 Terraform 的執行。我們需要將這種非阻塞性的行為特徵計入考量來決定採取何種檢查。

1.4.11.1.2.1. 輸出值與輸入參數

輸出值的 precondition 以及輸入變數的 validation 都可以對輸入輸出值進行斷言。

這些檢查是用來阻止 Terraform 在資料有問題時繼續執行的。

舉例來說,如果輸入參數的值是無效的那麼任由 Terraform 執行整個配置文件並沒有什麼意義,這種情況下,check 塊只會輸出有關無效輸入參數的警告,不會打斷 Terraform 的執行,而 validation 區塊則會警告輸入參數值非法,並終止 Terraform 執行 planapply 操作。

1.4.11.1.2.2. resource 區塊的 precondition 與 postcondition

check 塊與 preconditionpostcondition 的差異更加微妙。

precondition 是自訂條件檢查中最特殊的,因為它們是在資源的變更被計算或應用之前執行的檢查。決定使用 precondition 還是 postcondition 的考量也適用於選擇要使用 precondition 還是 check 區塊。

我們可以在 postconditioncheck 區塊之間互換來驗證資源和資料來源。例如,我們可以把上述範例中的 check 區塊改寫成 postcondition,以下的 postcondition 區塊將會驗證對 Terraform 網站的請求是否回傳了狀態碼 200

data "http" "terraform_io" {
  url = "https://www.terraform.io"

  lifecycle {
    postcondition {
        condition = self.status_code == 200
        error_message = "${self.url} returned an unhealthy status code"
    }
  }
}

checkpostcondition 區塊都在 planapply 操作中驗證了 Terraform 網站是否回傳 200 狀態碼,它們的差異是發生錯誤時的行為。

如果是 postcondition 失敗,那麼就無法繼續執行。Terraform 會阻止任意後續的 planapply 操作。

我們建議使用 check 區塊來驗證基礎設施的整體狀態,僅在希望確保單一資源狀態符合預期時才使用 postcondition


原簡體中文教程連結: Introduction.《Terraform入門教程》


上一篇
Day12-【入門教程】表達式
下一篇
Day14-【入門教程】Terraform 模組, 建立模組
系列文
Terraform 繁體中文25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言